beremiz%20%E5%9F%BA%E4%BA%8E%E5%B0%8F%E5%9E%8Bplc%20%E7%9A%84%E5%BC%80%E5%8F%91%E4%B8%8E%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E5%A4%84%E7%90%86%E8%AE%B2%E5%BA%A7
Beremiz 基于小型PLC 的开发与常见问题处理讲座
北京凯控科技有限公司 2023.4.14-2023.4.15
目录
- 关于IEC 61131-3
- Beremiz简介
- Beremiz的特点
- Beremiz的缺点
- kVPAC简介
- RTE运行时开发
关于IEC 61131-3
IEC 61131-3是由国际电工委员会(IEC)于1993年12月所制定IEC 61131标准的第3部分,用于规范可编程逻辑控制器(PLC),DCS,IPC,CNC和SCADA的编程系统的标准,应用IEC 61131-3标准已经成为工业控制领域的趋势。
该标准定义了PLC的软件结构、编程语言和程序执行方式,它综合了世界上广泛流行的编程语言的特点,并使其成为一种面向未来的PLC编程语言。
IEC 61131-3标准包括五种编程语言:梯形图(Ladder Diagram,LD)、指令表(Instruction List,IL)、顺序功能图(Sequential Function Chart,SFC)、结构化文本(Structured Text,ST)和功能块图(Function Block Diagram,FBD)。
IEC61131-3与传统PLC模型对比(1)
下面我们将IEC61131-3软件模型与传统的PLC模型(包括一个资源,运行一个任务,控制一个程序,且运行于一个封闭系统中)进行比较。
可以看出,IEC软件模型在传统PLC的软件模型的基础上增加了许多内容:
- IEC61131-3的软件模型是一种分层结构,每一层均隐含其下层的许多特征。
- 它奠定了将一个复杂的程序分解为若干个可以进行管理和控制的小单元,而这些被分解的小单元之间存在着清晰而规范的界面。
- 可满足由多个处理器构成的PLC系统的软件设计。
- 可方便地处理事件驱动的程序执行(传统的PLC的软件模型仅为按时间周期执行的程序结构)
- 对以工业通信网络为基础的分散控制系统(例如由现场总线将分布于不同硬件内的功能块构成一个具体的控制任务)尤其是软逻辑/PC控制这些正在发展中的新兴控制技术,该软件模型均可覆盖和适用。由此可见,该软件模型足以映像各类实际系统
IEC61131-3与传统PLC模型对比(2)
对于只有一个处理器的小型系统,其模型只有一个配置、一个资源和一个程序,与大多数PLC的情况完全相符。对于有多个处理器的中、大型系统,整个PLC被视作一个配置,每个处理器都用一个资源来描述,而一个资源则包括一个或多个程序。对于分散型系统,将包含多个配置,而一个配置又包含多个处理器,每个处理器用一个资源描述,每个资源则包括一个或多个程序。
IEC61131-3的优势与缺陷
IEC61131-3的优势在于它成功地将现代软件的概念和现代软件工程的机制用于PLC传统的编程语言。而它的不足却是因为它在体系结构上和硬件上依赖于传统的PLC的体系结构所形成的“先天不足”。
IEC61131-3的优势(1)
- 采用现代软件模块化原则。
- 编程语言支持模块化,将常用的程序功能划分为若干单元,并加以封装,构成编程的基础。
- 模块化时只设置必要的、尽可能少的输入和输出参数,尽量减少交互作用,尽量减少内部数据交换。
- 模块化接口之间的交互作用,均采用显性定义。
- 将信息隐藏于模块内,对使用者来讲只需了解该模块的外部特性(即功能,输入输出参数),而无需了解模块内算法的具体实现方法。
IEC61131-3的优势 (2)
- IEC61131-3支持自顶而下(top-down)和自底而上(bottom-up)程序开发方法。用户可先进行总体设计,将控制应用划分若干个部分,定义应用变量,然后编各个部分的程序:
这就是自顶而下。用户也可以先从底部开始编程,例如先导出函数和功能块,再进行按照控制要求编制程序:这是自底而上。 - 无论选择何种开发方法,IEC61131-3所创建的开发环境均会在整个编程过程中给予强有力的支持。
- IEC61131-3所规范的编程系统独立于任一个具体的目标系统,它可以最大限度地在不同的PLC目标系统中运行。这样就创造了一种具有良好开放性的氛围,奠定了PLC编程开放性的基础。
IEC61131-3的优势 (3)
- 将现代软件概念浓缩,并加以运用,例如:
- 数据使用DATA_TYPE说明机制
- 函数使用FUNTION说明机制
- 数据和函数的组合使用FUNTION_BLOCK说明机制。
Beremiz简介
Beremiz是一个用于机械自动化的集成开发环境(IDE),它是免费软件,符合IEC-61131等标准。它依赖于开放标准,独立于目标设备,并让您将任何处理器变成可编程逻辑控制器(PLC)。
Beremiz的一些特点包括:
- 它是免费软件,符合IEC-61131等标准。因此支持IEC-61131-3中描述的所有编程语言。这些语言包括梯形图(Ladder Diagram,LD)、指令表(Instruction List,IL)、顺序功能图(Sequential Function Chart,SFC)、结构化文本(Structured Text,ST)和功能块图(Function Block Diagram,FBD)。
PLC语言编辑器
PLC语言编辑器提供5种PLC语言和项目资源配置的编辑功能,分别是:

梯形图(LD)语言编辑器

功能块图(FBD)语言编辑器

顺序功能图(SFC)语言编辑器

结构化文本(ST)和指令表(IL)语言编辑器

项目资源配置编辑器
PLC语言的代码遵循IEC61131-3规定的格式保存到plc.xml文件中。
Beremiz的一些特点包括:
- Beremiz还包括一个IEC到C编译器,它可以将IEC-61131-3的文本形式编译为C代码。
- Beremiz通过插件的方式提供了:
- MODBUS库和组态工具
- EtherCat主站,非完整,不可运行
- 创建人机界面(HMI)的工具svgWeb和wxglade_hmi,实用价值不大
- CANopen支持
- Bacnet支持
- OPC-UA client
Beremiz存在的问题:
- 由于python环境的依赖,安装需要解决模块依赖问题,对于一般应用工程师安装难度较大,尤其在windows系统问题较多.
- 随着python主版本从2到3的变化,不少依赖模块存在过时,无法适应新的环境.
- 自带的rte环境较为简陋,并且依赖python环境,无法实现不同平台的迁移.
- 性能问题较突出,变量数和程序规模大到一定程度,系统响应速度大幅度下降,无法适应大型工程的应用.
kVPAC所进行的改造工作(1):
-
python2改为python3,wxPython升级到4.1
-
Beremiz原始代码是基于python2的,但是python2已经不再维护,因此需要将Beremiz的python2代码改为python3
-
Beremiz的图形界面基于WxPython,原始代码是基于wxPython2.8的,但是wxPython2.8已经不再维护,因此需要将Beremiz的wxPython2.8代码改为wxPython4.1
-
具体修改主要是变更函数和对象的名称和用法
kVPAC所进行的改造工作(2):
-
引入python3的asyncio异步模块
-
Beremiz在编译和其它耗时较多的操作时,界面会出现阻塞无响应的现象,因此将界面API进行了异步化改造.
-
引入异步支持,大幅度提高了界面响应速度,基本没有原版执行的停顿\卡死等现象。
kVPAC所进行的改造工作(3):
- python源码运行需要安装运行环境和大量相关模块,在不同的电脑系统上往往会出现各种兼容性问题,也不利于升级更新。所以采用了nuitka编译技术,将python源码转换为C代码后编译成二进制文件予以发布。不仅解决了运行环境问题,而且运行效率提升150倍,原来的功能块图、梯形图拖动时存在明显的滞后情形,编译后的二进制运行及其平滑。
kVPAC所进行的改造工作(4):
- 修复EtherCAT支持
- Beremiz源码中自带部分EtherCAT的编辑环境,不完整且无法正常使用
- 修复了编辑环境界面
- 新增了C代码生成器,用于输出操作代码
kVPAC所进行的改造工作(5):
- 新增基于platformio的编译系统
- 原始代码主要支持linux系统gcc编译系统,在windows系统需要执行安装、配置gcc编译器,无法达到工程使用的程度。
- PlatformIO是一个用于物联网开发的开源生态系统。它提供跨平台的开发环境和统一的调试器,还支持远程单元测试和固件更新。PlatformIO 是独立于平台运行的,实际上它只依赖于 python,然而 python 在 macOS、linux 和 windows 都能完美适配. 也就是说 PlatformIO 的工程从一个电脑很容易迁移到另一个电脑,只需要拷贝再使用PlatformIO 就能完美打开,不管团队中的成员使用什么操作系统
- PlatformIO 可以让工程共享变得异常简单。除此之外, PlatformIO 不仅可以在笔记本和台式机上运行,同样可以运行在没有显示桌面的服务器。PlatformIO的核心(PlatformIO Core) 就是一个终端程序, 它能配合多款IDE协同工作。
- PlatformIO的服务器、资源库都在国外,由于网速的原因,导致实际无法正常运行,因此将常用的资源迁移到国内服务器。
kVPAC所进行的改造工作(6): 新增基于Firmata协议的监控接口
- 原始代码仅支持在linux或windows系统中运行PLC程序,并且使用python作为基础运行平台,无法在其它不具备python的操作系统或无操作系统环境下运行。
- Firmata 是一种协议,用于通过计算机(或智能手机/平板电脑等)上的软件与微控制器进行通信。该协议可以在任何微控制器架构上的固件以及任何计算机软件包上的软件中实现。
- Firmata基于midi 消息格式,其中命令字节为 8 位,数据字节为 7 位。例如,midi通道压力(命令:0xD0)消息是 2 个字节长,在Firmata 中,命令 0xD0 用于启用数字端口(8 个引脚的集合)的报告。midi 和 Firmata 版本都是 2 个字节长,但含义明显不同。在Firmata 中,消息中的字节数必须符合相应的 midi 消息。然而,Midi System Exclusive (Sysex) 消息可以是任意长度,因此在整个 Firmata协议中使用最为显着。
- Firmata具有数十种不同语言的客户端库,覆盖大部分主流开发语言,使用极为便捷。
- 通过在运行时固件中嵌入Firmata代码,实现与组态软件的通信、监控调试等功能。
kVPAC的技术路线
- 吸收新技术,对Beremiz逐步重构,实现符合工业化标准的编程开发环境.
- 具体技术方面重点有以下考虑:
- 实现前后端模式,将软件界面与应用软件分离,可实现云平台方式的编程环境
- 重构底层架构,提升处理性能
- 重新设计C代码生成器,不再经过生成ST代码后再生成C代码,这样的话可以保持原始PLC语言与C代码的对应关系,以实现单步调试、错误跳转等功能。
PLC应用模块结构
-
PLC程序的核心部分是使用PLC语言编程的业务逻辑代码,PLC语言包括指令表、梯形图、顺序功能图、功能块图、结构化文本5种语言。
-
PLC流程

PLC时钟
- plc按照设定的运行周期定时运行
- 在plc_main.c中定义了____tick计数变量
- 每个PLC计算周期__tick加一
- PLC语言中的定时器和时间相关功能块依赖系统的当前时间,当前时间的精度决定了PLC程序中定时器和时间精度
- 在plc_main.c中定义了__CURRENT_TIME变量,TIME类型,此类型时间分辨率是ns,实际精度取决于系统的时钟分辨率,在STM32系统中可以达到uS级.
- 在每个PLC周期开始前,程序会调用PLC_GetTime函数更新__CURRENT_TIME变量,供功能块中的函数使用.
- 时间一般是当前的绝对时间(70年以来的秒数),有些没有时钟的系统是程序启动后的工作时间.
plc_app_abi_t接口
-
- plc_app_abi_t结构提供rte固件调用plc程序的操作接口.
- plc_app_abi_t结构定义在plc_app.c中
- plc_abi接口主要内容:
- 程序模块的内存分配信息,PLC模块没有系统加载时的初始化过程,需要这些信息进行PLC模块的初始化
- PLC的运行控制和debug调试函数init、start、stop等。
- plc程序的版本号、md5值等,用于版本校验。
typedef struct {
u32 *sstart; //链接后的启动地址
app_fp_t entry; //plc入口,仅链接用
u32 *data_loadaddr; //数据区(带初始值的变量)加载地址
u32 *data_start; //数据区起始地址
u32 *data_end; //数据区结束地址
u32 *bss_end; //中间变量区结束地址,起始地址跟随数据区
app_fp_t *pa_start; //初始化函数
app_fp_t *pa_end;
app_fp_t *ia_start;
app_fp_t *ia_end;
app_fp_t *fia_start;
app_fp_t *fia_end;
//RTE Version control
u32 rte_ver_major;
u32 rte_ver_minor;
u32 rte_ver_patch;
//Hardware ID
const char *hw_id;
//IO manager data
plc_loc_tbl_t *l_tab; //Location table
u16 l_sz; //Location table size
//Control instance of PLC_ID
const char *check_id;
//App interface
const char *id;
const char *app_name; //应用程序名称
u32 buildnumber; //应用程序构建编号
int (*start)(const void *); //plc启动函数 ,plc_rte_abi_t
int (*stop)(); //plc停止
void (*run)(); //plc运行,每周期调用一次
void (*config_init)(); //plc初始化
void (*dbg_resume)(); //debug启动
void (*dbg_suspend)(int); //debug挂起
int (*dbg_data_get)(u32 *, u32 *, void **); //读取debug数据
void (*dbg_data_free)(); //释放debug数据
void (*dbg_vars_reset)(u8); //复位debug变量
void (*dbg_var_register)(u32); //注册debug变量
void (*dbg_set_force)(u32, void *); //变量强制
u32 (*GetRetainSize)(); //获取retain大小
plc_data_t *data;
} plc_app_abi_t; ```
group 模块分配信息
RTE->PLC: sstart:启动地址
RTE->PLC:data_loadaddr:数据区(带初始值的变量)加载地址
RTE->PLC:data_start:数据区起始地址
RTE->PLC:data_end:数据区结束地址
RTE->PLC:bss_end:结束地址,起始地址跟随数据区
RTE->PLC:pa_start:初始化函数地址
RTE->PLC:pa_end:初始化函数结束地址
end
group 版本信息
RTE->PLC:rte_ver_major:RTE主版本号
RTE->PLC:rte_ver_minor:RTE次版本号
RTE->PLC:rte_ver_patch:RTE补丁版本号
RTE->PLC:check_id:PLC程序模块的MD5值
RTE->PLC:hw_id:硬件型号
RTE->PLC:app_name:PLC程序名称
RTE->PLC:buildnumber:PLC构建编号
end
group 运行控制函数
RTE->PLC: start:启动函数
RTE->PLC: stop:停止函数
RTE->PLC: run:plc运行,每周期调用一次
RTE->PLC: config_init:plc初始化
RTE->PLC: dbg_resume:debug启动
RTE->PLC: dbg_suspend:debug暂停
RTE->PLC: dbg_data_get:读取debug数据
RTE->PLC: dbg_data_free:释放debug数据
RTE->PLC: dbg_vars_reset:复位debug变量
RTE->PLC: dbg_var_register:注册debug变量
RTE->PLC: dbg_set_force:变量强制
RTE->PLC: GetRetainSize:获取retain(保持变量)大小
end
group 变量
RTE->PLC: l_tab:定位符表指针
RTE->PLC: l_tab_size:定位符表大小
RTE->PLC: data:变量表指针
end
变量与监控
-
由编程软件生成plc_debugger.c,定义了dbgvardsc变量表,这个变量表包含了PLC程序的所有变量.
-
每个变量并不直接是一个变量,而是一个变量控制块
//直接变量
typedef struct {\
IEC_##type value;\ //数值
IEC_BYTE flags;\ //debug、retain标志
IEC_##type old_value;\ //原始值,取消强制后从此恢复
IEC_##type fvalue;\ //强制值
} _IEC##type##_t;\
//带定位符的指针变量
typedef struct {\
IEC_##type *value;\ //指针
IEC_BYTE flags;\
IEC_##type *old_value;\
IEC_##type fvalue;\
} _IEC##type##_p; ```
-
编程软件同时生成VARIABLES.csv文件,文件内容与dbgvardsc相对应,读取变量时按照变量序号进行操作。
-
PLC监控的难点在于变量的类型不同,无法使用统一的接口来操作,所以采用的是按数据长度操作的办法.
-
读取PLC变量时,设置变量结构体中flags的debug标志,__publish_debug函数中将所有设置debug标志的变量复制到debug_buffer缓冲区,一次性由编程软件读取。
定位符
-
编程软件将PLC中使用的定位符变量输出到LOCATED_VARIABLES.h中,形式如下
-
__LOCATED_VAR(UINT,__IW3_1_0_94,I,W,3,1,0,94) ```
参数依次是数据类型、定位符名称和分解的定位符参数。
-
plc_app.c根据LOCATED_VARIABLES.h,生成plc_loc_table变量列表,将此列表加载到plc_app_abi_t结构体中
-
rte固件在plc计算周期的前后分别对plc_loc_table的输入、输出变量进行读取和赋值操作。
RTE_ABI接口
具体实现的机制是将RTE和PLC两个模块之间的交互提取为固定的ABI接口,这样RTE和PLC就可以分别开发和编译.
在windows和类linux环境下,RTE编译为可执行程序,PLC编译为动态链接库,运行时RTE动态加载PLC模块.
rte_abi接口的主要内容:
- plc需求的接口,如当前时间等
- 功能模块的接口,在plc程序中可以调用这些接口

<style scoped> section { text-align: center; } </style>
讲解完成 下面是答疑和展示.